using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

namespace DeveloperConsole
{
    public class ConCommand
    {
        // local variables
        public delegate void FunctionCall();
        private string m_sKey;
        private string m_sDescription;
        private ConsoleFlags m_flags;

        // New stuff
        private MethodInfo m_methodInfo;

        public ConCommand(string key, Type type, string functionName, ConsoleFlags flags, string description)
        {
            m_methodInfo = type.GetMethod(functionName);
            m_sKey = key;
            m_sDescription = description;
            m_flags = flags;
            ConCommandMap.Singleton.CreateConCommand(m_sKey, this);
        }
        public void Invoke(Object[] objects)
        {
            if (m_methodInfo != null)
            {
                ParameterInfo[] theParams = m_methodInfo.GetParameters();
                int iNumberOfParams = theParams.Length;
                if (iNumberOfParams == 0)
                    m_methodInfo.Invoke(null, null);
                else
                {
                    if (objects == null || objects.Length != iNumberOfParams)
                        return; // Not enough arguments passed in to match the method being invoked.
                    try
                    {
                        for (int i = 0; i < iNumberOfParams; i++)
                            objects[i] = Convert.ChangeType(objects[i], (theParams[i].ParameterType));
                    }
                    catch (Exception e) { DeveloperConsole.Singleton.DevMsg("Improper Parameters.\n");  }
                    try
                    {
                        m_methodInfo.Invoke(null, objects);
                    }
                    catch (Exception e) { DeveloperConsole.Singleton.DevMsg("Improper Parameters - 2.\n"); }
                }
            }
        }
        public string GetKey()
        {
            return m_sKey;
        }
        public string GetDescription()
        {
            return m_sDescription;
        }
        public ConsoleFlags GetFlags()
        {
            return m_flags;
        }
        public int GetNumberOfParams()
        {
            if (m_methodInfo != null)
            {
                return m_methodInfo.GetParameters().Length;
            }
            return -1;
        }
    }

    public sealed class ConCommandMap
    {
        private static readonly ConCommandMap instance = new ConCommandMap();
        private static Dictionary<string, ConCommand> m_Map = new Dictionary<string, ConCommand>();

        private ConCommandMap() { }

        public static ConCommandMap Singleton
        {
            get
            {
                return instance;
            }
        }
        public bool CreateConCommand(string key, ConCommand value)
        {
            if (m_Map == null)
                m_Map = new Dictionary<string, ConCommand>();
            if (!string.IsNullOrEmpty(key))
            {
                m_Map.Add(key, value);
                return true;
            }
            return false;
        }
        public ConCommand GetConCommand(string key)
        {
            if (m_Map.ContainsKey(key))
                return m_Map[key];
            return null;
        }
        public bool SetConCommand(string key, ConCommand value)
        {
            if (m_Map.ContainsKey(key))
            {
                m_Map[key] = value;
                return true;
            }
            return false;
        }
        // Only detects complete/exact string. No variation atm.
        public List<string> Find(string key)
        {
            List<string> theKeys = new List<string>();
            foreach (KeyValuePair<string, ConCommand> pair in m_Map)
            {
                if (pair.Value.GetDescription().Contains(key) ||
                    pair.Key.Contains(key))
                    theKeys.Add(pair.Key);
            }
            return theKeys;
        }
        public List<string> Keys()
        {
            List<string> theKeys = new List<string>();
            foreach (KeyValuePair<string, ConCommand> pair in m_Map)
            {
                theKeys.Add(pair.Key);
            }
            return theKeys;
        }
        public bool Invoke(string key, object []objects)
        {
            ConCommand command = GetConCommand(key);
            if (command != null)
            {
                command.Invoke(objects);
                return true;
            }
            return false;
        }
    }
}